 /**                                                                                                            
 * Copyright 2012-2013 Paul Carff <paul@anzym.com>                     
 *                                                                                                             
 * This file is part of the KegDroid project.               
 * For more information on Betsy, see http://www.anzym.com/kegdroid                     
 *                                                                                                             
 * Kegdroid is free software: you can redistribute it and/or modify               
 * it under the terms of the GNU General Public License as published by         
 * the Free Software Foundation, either version 2 of the License, or            
 * (at your option) any later version.                                                                         
 *                                                                                                             
 * Kegdroid is distributed in the hope that it will be useful,                    
 * but WITHOUT ANY WARRANTY; without even the implied warranty of               
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                
 * GNU General Public License for more details.                                                                
 *                                                                                                             
 * You should have received a copy of the GNU General Public License            
 * along with Kegdroid.  If not, see <http://www.gnu.org/licenses/>.              
 */

//  Includes
#include <Max3421e.h>
#include <Usb.h>
#include <AndroidAccessory.h>
#include <SoftwareSerial.h>
#include <sm130.h>
#include <OneWire.h>

/* Need to be careful how I am using thse - the 
 * the pinnouts seem to be switching on me.
 */
#define TAP_0_PIN 8 
#define TAP_1_PIN 9  
#define FLOW_0_PIN  20    // Interrupt 3 
#define FLOW_1_PIN  21    // Interrupt 2


// Interrupt Numbers
#define INTERRUPT_FLOW_0 3 // Pin D20  (using attachInterrupt() )
#define INTERRUPT_FLOW_1 2 // Pin D21  (using attachInterrupt() )

#define TEMP_SENSOR_PIN 2
#define COUNTS_PER_OZ 173

//#define HEAD_STEPPER_MOTOR_PIN

// Android ADK
AndroidAccessory acc("Anzym Enterprises",
	"V2",
	"KegDroid",
	"1.0",
	"http://anzym.com",
        "0000000000000001");
            
//  Globals

// Possible States of System
enum SystemState
{
  SCANBADGE,
  PREPARE2POUR,
  POURING,
  CLEANUP,
  INVALID
};

// set simulateFlow to TRUE if want to simulate flow - don't have flow sensors.
bool simulateFlow = false;

SystemState KegDroidState;
int savedFlowCount;
int pourVolume;
int currentVolume = 0;
int tapNumber = 3;
int flowTimeoutCounter = 0;
int maxFlowTimeountCounter = 50;
int reportCounter = 0;

int newFlowCount = 0;

OneWire temp(TEMP_SENSOR_PIN);
// <TODO> Think about what these need to be.
bool tapState[2] = {false, false};
bool authenticated = false;

// Flow Variables.
volatile int flow0 = 0;
volatile int flow1 = 0;

//RFID Tags - for testing
uint8_t myTag[7] = "";
//uint8_t nfcTag1[7] = "B279F1";
//uint8_t nfcTag2[7] = 669624457;

//-----------------------------------------------------
// Setup Code
//-----------------------------------------------------
void setup() {
  Serial.begin(115200);  // USB (can this be higher?)
  //adapter.begin(19200);
  
  // flow meters
  //pinMode(FLOW_0_PIN, INPUT);
  //pinMode(FLOW_1_PIN, INPUT);
  digitalWrite(FLOW_0_PIN, HIGH);
  digitalWrite(FLOW_1_PIN, HIGH);
  attachInterrupt(INTERRUPT_FLOW_0, countFlow0, RISING);
  attachInterrupt(INTERRUPT_FLOW_1, countFlow1, RISING);
  
  //  solenoid valves
  pinMode(TAP_0_PIN, OUTPUT);
  pinMode(TAP_1_PIN, OUTPUT);
  closeTaps();
  Serial.println("Closed Taps");
  //  adk
  Serial.println("Checking if connected " );
  Serial.println(acc.isConnected());
  
  acc.powerOn();
  Serial.println("Initialized");
  delay(1000);
  // Print firmware version
  char buf[100];
  Serial.println("Reading");
  //resetRFID();
  //reader.reset();
  //int len = reader.receive_raw((uint8_t*)buf);
  //Serial.print("Read: ");
  //Serial.println(len);
  //buf[len] = 0;
  //Serial.print("buf: ");
  //Serial.println(buf);
  Serial.println("Changing State to PREPARE2POUR");
  KegDroidState = PREPARE2POUR;
}


//-----------------------------------------------------
// Main Loop
//-----------------------------------------------------
void loop() {
  
  // Check if connected to tablet
  if (acc.isConnected()) {
    // Check State
    //Serial.print("KegDroidState ");
    //Serial.println(KegDroidState);
    switch (KegDroidState)
    {
       case PREPARE2POUR:  // PREPARE2POUR State
       {
         // clear flowTimeoutCounter
         flowTimeoutCounter = 0;
         // prepare to pour state
         prepare2Pour();

         break;
       } // end PREPARE2POUR state
       case POURING:   // POURING State
       {
         /*
         *  FOR SIMULATION ONLY    *
         *  ADD a flag to set for this loop.
         */
        
         if (simulateFlow) {
           switch (tapNumber) 
           {
             case(0):
             {
               flow0 += 50;
               break;
             }
             case(1):
             {
               flow1 += 50;
               break;
             }
             
           }
         }
         
         ///CHECKING FOR A CLOSE TAPS COMMAND         
         byte msg[10];
         Serial.println("POURING - checking for a closeTapCommand ");
         int len = acc.read(msg, sizeof(msg), 1); // read data into msg variable
         //Serial.print("prep2pour msg length: ");
         //Serial.println(len);
         if (len > 0) {
            if (msg[0] == 0xC) { // compare received data
              Serial.print("POURING - Received close command ");
              Serial.println(msg[0]);
              Serial.println("POURING - Changing to CLEANUP state");
              KegDroidState = CLEANUP;
            }
         } 

         
         // get flow count
         newFlowCount = getFlow(tapNumber);
         currentVolume = newFlowCount;
         // send volume message
         sendVolume(tapNumber);
         // if flow is greater than volume
         Serial.print("POURING - CurrentVolume: ");
         Serial.println(currentVolume);
         Serial.print("POURING - pourVolume: ");
         Serial.println(pourVolume * COUNTS_PER_OZ);
         
         if (currentVolume >= (pourVolume * COUNTS_PER_OZ)) {
           Serial.println("POURING - maxed on the pour");
           Serial.println("POURING -Changing State to CLEANUP ");
           KegDroidState = CLEANUP;
         } 
         else
         {
           if( newFlowCount == savedFlowCount) // flow has not increased
           {
             // increase flowTimeoutCounter
             flowTimeoutCounter++;
             Serial.print("POURING - flowTimeoutCounter: ");
             Serial.println(flowTimeoutCounter);
             if( flowTimeoutCounter >= maxFlowTimeountCounter )
             {  // flowTimeoutCounter is greater than max timeout value
               // set new state (CLEANUP)
               Serial.println("POURING - Exceeded Wait Timeout");
               Serial.println("POURING - Changing State to CLEANUP");
               KegDroidState = CLEANUP;
             }
           }
           else // flow did increase
           {
             // clear flowTimeoutCounter
             flowTimeoutCounter = 0;
             }
         }
         // save newFlowCount as savedFlowCount
         savedFlowCount = newFlowCount;
         break;
       } // end POURING state
       case CLEANUP:   // CLEANUP State
       {
         // close taps
         Serial.println("CLEANUP - closing taps");
         closeTaps();
         sendVolume(tapNumber);
         sendFinalVolume(tapNumber);
         tapNumber = 3;
         
         // set new state (scanBadge)
         Serial.println("CLEANUP - changing State to PREPARE2POUR");
         KegDroidState = PREPARE2POUR;
         Serial.println("CLEANUP - delay");
         delay(100);
         break;
       } // end CLEANUP state
    } // end Check State
    delay(100);

    // only send a mesurement report every five calls
    if( (reportCounter % 5) == 0 )
    { 
      // get temperature
      sendTemp();
    }
    
  } // end ifConnected 
} // end Main Loop



bool sendTag(Tag tag) {
  byte rfmsg[10];
  
  Serial.print("tag.serial_size: ");
  Serial.println(tag.serial_size);
  Serial.print("Detected tag with type: ");
  Serial.print(tag.type, HEX);
  Serial.print(", ID: ");
  for(int i = 0; i < tag.serial_size; i++) {
    myTag[i] = tag.serial[i];
    Serial.print(tag.serial[i], HEX);
  }
  Serial.println();
  //--------------------
  // send tag to android 
  //--------------------
  rfmsg[0] = 0x7;
  Serial.println("sendTag - sent 0x7");
  for (int ctr = 1; ctr <= tag.serial_size; ctr++) {
    rfmsg[ctr] = tag.serial[ctr-1];
    //Serial.write(rfmsg[ctr], HEX);
  } 
  Serial.println("Writing to acc");
  acc.write(rfmsg, 5);
  
}


//--------------------------------
// Read command from android app
//--------------------------------
bool prepare2Pour()
{
//         if tap/volume message
//         {
//           if tap value < 0 (INVALID card)
//             set new state (PREPARE2POUR)
//           else (valid card)
//             reset flow counters
//             set flowTimeoutCounter to zero
//             set flow limit from volume
//             open given tap
//             set new state (POURING)
//         }
  byte msg[10];
  //Serial.println("standing by for tap and volume");
  int len = acc.read(msg, sizeof(msg), 1); // read data into msg variable
  //Serial.print("prep2pour msg length: ");
  //Serial.println(len);
  if (len > 0) {
    if (msg[0] == 0xA) { // compare received data
      Serial.println("0xA");
      int tn = msg[1];
      if (tn != -1) {
      	tapNumber = tn;
        pourVolume = msg[2];
        Serial.print("POURING ");
        Serial.print(pourVolume);
        Serial.println(" oz.");
        resetFlow();
        openTap(tapNumber);  // Open Tap
        tapState[tapNumber] = true;
        Serial.print("Opening Tap ");
        Serial.println(tapNumber);
        Serial.println("Changing State to POURING");
        KegDroidState = POURING;
      } else {
      	//send final data and then close taps
        Serial.println("prepare to pour - sending final volume.");
        sendFinalVolume(tapNumber);
        Serial.println("prepare to pour - closing taps");
        closeTaps(); // Close Taps
      }
    }
     if (msg[0] == 0xD) { // canceling pour
       //Bypassing SCAN so set these to PREPARE2POUR.
       Serial.println("Received cancel pour msg - Changing State to PREPARE2POUR");
       KegDroidState = PREPARE2POUR;
       delay(100);
     }
  }
}

void sendTemp() {
    uint16_t  temperature = (uint16_t) getTemp();
  
    byte msg[3];
    //Serial.print("TemperatureC: ");
    //Serial.println(temperature);
    float tempF = (getTemp() * (9.0/5.0)) + 32.0;
    //Serial.print("TemperatureF: ");
    //Serial.println(tempF);
    msg[0] = 0x4;
    msg[1] = temperature >> 8;
    msg[2] = temperature & 0xff;
    acc.write(msg, 3);
}

float getTemp(){
  //returns the temperature from one DS18S20 in DEG Celsius

  byte data[12];
  byte addr[8];

  if ( !temp.search(addr)) {
      //no more sensors on chain, reset search
      temp.reset_search();
      return -1000;
  }

  if ( OneWire::crc8( addr, 7) != addr[7]) {
      //Serial.println("CRC is not valid!");
      return -1000;
  }

  if ( addr[0] != 0x10 && addr[0] != 0x28) {
      Serial.print("Device is not recognized");
      return -1000;
  }

  temp.reset();
  temp.select(addr);
  temp.write(0x44,1); // start conversion, with parasite power on at the end

  byte present = temp.reset();
  temp.select(addr);    
  temp.write(0xBE); // Read Scratchpad

  
  for (int i = 0; i < 9; i++) { // we need 9 bytes
    data[i] = temp.read();
  }
  
  temp.reset_search();
  
  byte MSB = data[1];
  byte LSB = data[0];

  float tempRead = ((MSB << 8) | LSB); //using two's compliment
  float TemperatureSum = tempRead / 16;
  
  return TemperatureSum;
  
}

void sendVolume(int tapNumber) {
  //int val = getFlow(tapNumber)/COUNTS_PER_OZ;
  int val = getFlow(tapNumber);
  byte msg [4];
  Serial.print("Flow Volume tap #");
  Serial.print(tapNumber);
  Serial.print(" ");
  Serial.println(val);
  msg[0] = 0x5;
  msg[1] = (byte)tapNumber;
  msg[2] = val >> 8;
  msg[3] = val & 0xff;
  acc.write(msg, 4);
}

void sendFinalVolume(int tapNumber) {
  //int val = getFlow(tapNumber)/COUNTS_PER_OZ;
  int val = getFlow(tapNumber);
  byte msg [4];
  Serial.print("Flow Volume tap #");
  Serial.print(tapNumber);
  Serial.print(" ");
  Serial.println(val);
  msg[0] = 0x6;
  msg[1] = (byte)tapNumber;
  msg[2] = val >> 8;
  msg[3] = val & 0xff;
  acc.write(msg, 4);
}


// Tap Methods
void openTap(int tapNumber) {
  tapState[tapNumber] = true;
  switch (tapNumber) {
      case 0:
        digitalWrite(TAP_0_PIN, HIGH);
        break;
      case 1:
        digitalWrite(TAP_1_PIN, HIGH);
        break;
      default:
        break;
  }
}

void closeTap(int tapNumber) {
  tapState[tapNumber] = false;
  switch (tapNumber) {
      case 0:
        
        digitalWrite(TAP_0_PIN, LOW);
        break;
      case 1:
        digitalWrite(TAP_1_PIN, LOW);
        break;
      default:
        break;
  }
}

void closeTaps() {
  closeTap(0);
  closeTap(1);
  tapState[0] = false;
  tapState[1] = false;
}

// Flow Meter Methods
void resetFlow() {
  flow0 = 0;
  flow1 = 0;
  savedFlowCount = 0;
}

int getFlow(int tapNmbr) {
  if (tapNmbr == 0) {
    return flow0;
  }else if (tapNmbr == 1) {
    return flow1;
  }
  return -1;
}

void countFlow0() {
   flow0++;
  // (float) flow_volume = flow1/172.9   //in ml
}

void countFlow1() {
   flow1++;
  // (float) flow_volume = flow1/172.9   //in ml
}


